﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace RayTracer.Kernels
{
    /// <summary>
    /// Beinhaltet den OpenCL Code der Kernel.
    /// </summary>
    partial class KernelSimpleLightPrimitiveSphere
    {
        private const string KERNEL_MAIN = "SimpleLightPrimitiveSphere";

        /*************
        *  
        *  Eingabe:
        *  surInter    Der Zielschnittpunkt                    [i]
        *  surNormal   Der Richtung der Fläche.                [i]//wird nicht benötigt
        *  oldLigInvis Nichtsichtbarkeit bei vorherigen Tests  [i*ligCount+l]
        *              mit anderen Primitiven   
        *  
        *  ligCount    Anzahl der Lichter L.
        *  ligPos      Position des Lichts.                    [l]
        *  
        *  objCount    Anzahl der Objekte M.
        *  objPos      Position der Kugel.                     [m]
        *  objRot      Rotation der Kugel.                     [m]//wird nicht benötigt
        *  objRadius   Radius der Kugel.                       [m]
        *  
        * 
        *  Ausgabe:
        *  ligInvis    Indikator ob Licht nicht sichtbar.      [i*ligCount+l]
        *  ligDir      Die Einfallsrichtung des Lichts.        [i*ligCount+l]
        *  
        *************/
        private const string KERNEL_SOURCE = @"
            kernel void SimpleLightPrimitiveSphere(
                global  read_only float4* surInter,
                global  read_only float4* surNormal,
                global  read_only bool* oldLigInvis,

                int ligCount,
                global  read_only float4* ligPos,
    
                int objCount,
                global  read_only float4* objPos,
                global  read_only float4* objRot,
                global  read_only float* objRadius,
    

                global  write_only bool* ligInvis,
                global  write_only float4* ligDir
            )
            {
                int i = get_global_id(0);
                for(int l = 0; l < ligCount; l++){
                    float4 tempLigDir = ligPos[l] - surInter[i];
                    float distanceToLight = length(tempLigDir);
                    tempLigDir = tempLigDir / distanceToLight; //Normalisieren
                    bool invisible = oldLigInvis[i * ligCount + l];

                    if (!invisible) {       //Falls das Licht sichtbar ist, prüfe ob ein Schnittpunkt mit einem Objekt existiert.
                        for(int o = 0;o < objCount;o++) {
                            if (!invisible) {
                                //Falls das Licht durch einen vorherigen Durchgang der Schleife nicht als verdeckt gilt,
                                //Prüfe ob der Strahl das Objekt schneidet. Schnittpunktberechnung aus KernelPrimitiveSphere.CL.cs
   
                                // M = Objektposition
                                // O = Schnittpunkt
                                // r = Vektor des Lichtstrahls
                                // a= r_1^2 + r_2^2 + r_3^2
                                // b= 2((O_1-M_1)r_1+(O_2-M_2)r_2+(O_3-M_3)r_3)
                                // c= ((O_1-M_1)^2 + (O_2-M_2)^2 + (O_3-M_3)^2) - radius^2

                                float4 v = surInter[i] - objPos[o]; // Vektor ausgehend von dem Schnittpunkt zum Zentrum des Objekts
                                float a = tempLigDir.x * tempLigDir.x + tempLigDir.y * tempLigDir.y + tempLigDir.z * tempLigDir.z;
                                float b = 2.0f * dot(v, tempLigDir);
                                float c = v.x * v.x +
                                          v.y * v.y +
                                          v.z * v.z - 
                                          objRadius[o] * objRadius[o];

                                float det = b * b - 4 * a * c;  // Determinante der Mitternachtsformel(b^2-4ac)
                                float s1 = -1.0f;
                                float s2 = -1.0f;

                                if(det < 0.0f){
                                    // Kein Schnittpunkt, wenn Determinanten kleiner 0
                                    continue;
                                } else if (det == 0.0f) {
                                    // Ein Schnittpunkt, wenn Determinante gleich 0
                                    s1=(-b) / (2 * a);
                                } else if (det > 0.0f) {
                                    // Zwei Schnittpunkte, wenn Determinante größer 0
                                    // Kleineres der beiden s gesucht, welches größer 0 ist
                                    // negatives s bedeutet, dass Schnittpunkt hinter dem Ausgangspunkt liegt
                                    s1 = (-b + sqrt(det)) / (2 * a);
                                    s2 = (-b - sqrt(det)) / (2 * a);
                                }

                                if((s1 > 0.01f && s1 < distanceToLight) ||
                                   (s2 > 0.01f && s2 < distanceToLight)) {
                                    invisible = true;
                                }
                            }
                        }
                    }
                    ligInvis[i * ligCount + l] = invisible;
                    ligDir[i * ligCount + l] = tempLigDir;
                } 
            }
            ";
    }
}
